1 package xdoclet.sdk.xgg.binding;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import xdoclet.XDocletException;
7
8 /***
9 * Utility class that converts from xml type names to java names. This
10 * class is largely inspired from Zeus.
11 *
12 * @author <a href="mailto:aslak.hellesoy at bekk.no">Aslak Hellesøy</a>
13 * @version $Revision: 1.4 $
14 */
15 public class NamingUtils {
16 /*** A map of illegal characters mapped to legal alternative representations. */
17 private static Map illegalCharMap = new HashMap();
18
19 /*** A map of all the Java reserved words. */
20 private static HashMap reservedWords = new HashMap();
21
22 /*** An array of all the Java reserved words to be stored in a map. */
23 private static String reservedWordsArray[] = {"abstract",
24 "boolean", "break", "byte", "case", "catch", "char", "class",
25 "char", "const", "continue", "default", "do", "double", "else",
26 "extends", "false", "final", "finally", "float", "for",
27 "goto", "if", "implements", "import", "instanceof", "int",
28 "interface", "long", "native", "new", "null", "package",
29 "private", "protected", "public", "return", "short", "static",
30 "super", "switch", "synchronized", "this", "throw", "throws",
31 "transient", "true", "try", "void", "volatile", "while",
32
33 // This is a quick hack to get around name clashes with xjavadoc.XTag
34 // It should be removed, and XTag.getName() and XTag.getValue() should
35 // be renamed to getTagName() and getTagValue() to minimise the risk
36 // of name clashes.
37 "name", "value"
38 };
39
40 /*** The default alternative representation for illegal characters. */
41 public final static String DEFAULT_MAPPING = "";
42
43 /*** Java Name for PCDATA variable */
44 public final static String PCDATA_JAVA_NAME = "value";
45 /*** XML Name for PCDATA elements */
46 public final static String PCDATA_XML_NAME = "PCDATA";
47
48 /*** Prefix to append to names that clash with Java reserved words. */
49 public final static String XML_PREFIX = "xml";
50
51
52 /***
53 * <p>
54 * Prevent direct instantiation.
55 * </p>
56 */
57 private NamingUtils() { }
58
59
60 /***
61 * <p>
62 * This will take a <code>String</code> with unknown capitalization,
63 * and convert all the letters to lowercase letters.
64 * Note that if you want just the first
65 * letter converted to lowercase, then
66 * <code>{@link #initialLower(String)}</code> should be used.
67 * </p><p>
68 * And for all you geniuses out there, yes, I know that
69 * <code>toLowerCase()</code> can be used directly; but
70 * placing the code within this method allows this class
71 * to be uniformly used for all capitalization purposes.
72 * </p>
73 *
74 * @param original <code>String</code> to convert.
75 * @return <code>String</code> - the converted <code>String</code>.
76 */
77 public static String allLower(String original) {
78 if (original == null) {
79 throw new IllegalArgumentException("A non-null String must be " +
80 "supplied to CapitalizationUtils methods.");
81 }
82
83 return original.toLowerCase();
84 }
85
86
87 /***
88 * <p>
89 * This will take a <code>String</code> with unknown capitalization,
90 * and convert all the letters to uppercase letters.
91 * Note that if you want just the first
92 * letter converted to lowercase, then
93 * <code>{@link #initialUpper(String)}</code> should be used.
94 * </p><p>
95 * And for all you geniuses out there, yes, I know that
96 * <code>toUpperCase()</code> can be used directly; but
97 * placing the code within this method allows this class
98 * to be uniformly used for all capitalization purposes.
99 * </p>
100 *
101 * @param original <code>String</code> to convert.
102 * @return <code>String</code> - the converted <code>String</code>.
103 */
104 public static String allUpper(String original) {
105 if (original == null) {
106 throw new IllegalArgumentException("A non-null String must be " +
107 "supplied to CapitalizationUtils methods.");
108 }
109
110 return original.toUpperCase();
111 }
112
113
114 /***
115 * <p>
116 * Returns the value that the given key is mapped to. This value will
117 * replace any occurence of the key when a name contains illegal
118 * characters for a Java identifier.
119 * </p>
120 *
121 * @param key the key mapped to the value to return.
122 * @return <code>String</code> - the value the given key is mapped to.
123 */
124 public static String getCharMapping(Character key) {
125 if (key == null) {
126 throw new IllegalArgumentException("A non-null Character must be " +
127 "supplied to NamingUtils methods.");
128 }
129
130 return (String) illegalCharMap.get(key);
131 }
132
133
134 /***
135 * <p>
136 * Returns the conventional Java class name from an XML name. The initial
137 * character is capitalized, and any illegal characters are replaced.
138 * There is no check for a collision with a Java reserved word because
139 * all Java reserved words have their initial character lower case.
140 * </p><p>
141 * For example, calling <code>getJavaClassName("my-element")</code> would
142 * return "MyElement" as the Java class name.
143 * </p>
144 *
145 * @param xmlName the XML name to convert to a Java class name.
146 * @return <code>String</code> - the converted Java name.
147 */
148 public static String getJavaClassName(String xmlName) {
149 if (xmlName == null) {
150 throw new IllegalArgumentException("A non-null XML name must be " +
151 "supplied to NamingUtils methods.");
152 }
153
154 String result = initialUpper(
155 convertIllegalChars(convertReservedWord(xmlName)));
156
157 return result;
158 }
159
160
161 /***
162 * <p>
163 * Returns the Java variable name from a Java class name assuming that the
164 * variable will be a collection based variable (i.e. ending with "List"
165 * as in "myVariableList"). The initial character is uncapitalized, and
166 * any illegal characters are replaced. There is no check for a
167 * collision with a Java reserved word because the assumption is that
168 * the variable has some word appended to it that identifies the fact
169 * that the variable denotes a collection.
170 * </p><p>
171 * For example, calling
172 * <code>getJavaCollectionVariableName("MyClass")</code> would return
173 * "myClass" as the Java variable name.
174 * </p>
175 *
176 * @param className the class name to convert to a collection based
177 * variable name.
178 * @return <code>String</code> - the converted Java variable name.
179 */
180 public static String getJavaCollectionVariableName(String className) {
181 if (className == null) {
182 throw new IllegalArgumentException("A non-null class name must " +
183 "be supplied to NamingUtils methods.");
184 }
185
186 return convertReservedWord(
187 convertIllegalChars(initialLower(className)));
188 }
189
190
191 /***
192 * <p>
193 * Returns a conventional Java name from an XML name. Any illegal
194 * characters are replaced. Collision with reserved words is <i>not</i>
195 * checked, as this is not used directly in class generation.
196 * </p><p>
197 * For example, calling <code>getJavaName("my-element")</code> would
198 * return "myElement" as the Java class name.
199 * </p>
200 *
201 * @param xmlName the XML name to convert to a Java name.
202 * @return <code>String</code> - the converted Java name.
203 */
204 public static String getJavaName(String xmlName) {
205 if (xmlName == null) {
206 throw new IllegalArgumentException("A non-null XML name must be " +
207 "supplied to NamingUtils methods.");
208 }
209
210 // Handle the PCDATA special case
211 if (xmlName.equals(PCDATA_XML_NAME)) {
212 return convertIllegalChars(PCDATA_JAVA_NAME);
213 }
214
215 return convertReservedWord(convertIllegalChars(xmlName));
216 }
217
218
219 /***
220 * <p>
221 * This will handle conversion from an XML type to a Java type. It first
222 * attempts to perform an XML Schema type to Java type conversion. For
223 * example, if the XML type is "string", it would convert this to
224 * the Java type "String". If that is unsuccessful, it returns the
225 * type unchanged, assuming that it is a proper Java class name.
226 * </p>
227 *
228 * @param xmlType the XML type to convert to a Java type.
229 * @return <code>String</code> - the converted Java type.
230 */
231 public static String getJavaType(String xmlType) {
232 String javaType;
233 try {
234 javaType = getSchemaJavaType(xmlType);
235 return javaType;
236 } catch (XDocletException e) {
237 e.printStackTrace();
238 }
239
240 // Ensure a legal Java class name is returned
241 return getJavaClassName(xmlType);
242 }
243
244
245 /***
246 * <p>
247 * Returns the conventional Java variable name from an XML name. The
248 * initial character is uncapitalized, and any illegal characters are
249 * replaced. If there is a collision with a Java reserved word, the name
250 * is prefixed with <code>{@link #XML_PREFIX}</code>.
251 * </p><p>
252 * For example, calling <code>getJavaVariableName("XmlName")</code>
253 * would return "xmlName" as the Java variable name.
254 * </p>
255 *
256 * @param xmlName the XML name to convert to a variable name.
257 * @return <code>String</code> - the converted Java variable name.
258 */
259 public static String getJavaVariableName(String xmlName) {
260 if (xmlName == null) {
261 throw new IllegalArgumentException("A non-null XML name must " +
262 "be supplied to NamingUtils methods.");
263 }
264
265 // Handle the PCDATA special case
266 if (xmlName.equals(PCDATA_XML_NAME)) {
267 return convertReservedWord(
268 initialLower(PCDATA_JAVA_NAME));
269 }
270
271 return convertReservedWord(
272 convertIllegalChars(initialLower(xmlName)));
273
274 }
275
276
277 /***
278 * <p>
279 * This will take as input an XML Schema data type (such as "string")
280 * and convert it to a Java data type (such as "String"). It includes,
281 * if needed, the fully-qualified package (such as "java.util.List") to
282 * prevent import problems.
283 * </p>
284 * <p><b>Note:</b> Eventually this should probably work in concert with
285 * some methodology that handles package imports and the like.</p>
286 *
287 * @param schemaType the XML Schema data type (as a <code>String</code>)
288 * @return <code>String</code> - the Java data type.
289 * @throws XDocletException - when the supplied
290 * schema type is not supported.
291 */
292 public static String getSchemaJavaType(String schemaType)
293 throws XDocletException {
294
295 if (schemaType == null) {
296 throw new IllegalArgumentException("A non-null schema type must " +
297 "be supplied to SchemaUtils methods.");
298 }
299
300 if (schemaType.equals("string")) {
301 return "java.lang.String";
302 }
303
304 // If we got here, it's an unsupported or incorrect data type
305 throw new XDocletException(schemaType);
306 }
307
308
309 /***
310 * <p>
311 * Returns the XML name from a typical accessor method. The naming
312 * convention for such a method is "get<variable-name>". The first
313 * three characters are discarded, and the initial character is
314 * uncapitalized.
315 * </p><p>
316 * For example, calling
317 * <code>getXMLElementNameFromAccessor("getMyVariable")</code> would
318 * return "myVariable" as the XML name. This method would also work
319 * with typical mutator methods.
320 * </p>
321 *
322 * @param accessor the name of the accessor method to derive the XML name
323 * from.
324 * @return <code>String</code> - the converted XML name.
325 */
326 public static String getXMLElementNameFromAccessor(String accessor) {
327 if (accessor == null) {
328 throw new IllegalArgumentException("A non-null accessor must " +
329 "be supplied to NamingUtils methods.");
330 }
331
332 return initialLower(accessor.substring(3));
333 }
334
335
336 /***
337 * <p>
338 * This will take a <code>String</code> with unknown capitalization,
339 * and convert the first letter to a lowercase letter, while leaving
340 * all other letters the same. Note that if you want the rest of
341 * the letters converted to lowercase, then
342 * <code>{@link #allLower(String)}</code> should be used.
343 * </p>
344 *
345 * @param original <code>String</code> to convert.
346 * @return <code>String</code> - the converted <code>String</code>.
347 */
348 public static String initialLower(String original) {
349 if (original == null) {
350 throw new IllegalArgumentException("A non-null String must be " +
351 "supplied to CapitalizationUtils methods.");
352 }
353 if (original.length() == 0) {
354 return original;
355 }
356
357 char[] originalChars = original.toCharArray();
358 char firstLetter =
359 Character.toLowerCase(original.charAt(0));
360 originalChars[0] = firstLetter;
361
362 return new String(originalChars);
363 }
364
365
366 /***
367 * <p>
368 * This will take a <code>String</code> with unknown capitalization,
369 * and convert the first letter to a capital letter, while leaving
370 * all other letters the same. Note that if you want the rest of
371 * the letters converted to lowercase, then
372 * <code>{@link #justInitialUpper(String)}</code> should be used.
373 * </p>
374 *
375 * @param original <code>String</code> to convert.
376 * @return <code>String</code> - the converted <code>String</code>.
377 */
378 public static String initialUpper(String original) {
379 if (original == null) {
380 throw new IllegalArgumentException("A non-null String must be " +
381 "supplied to CapitalizationUtils methods.");
382 }
383 if (original.length() == 0) {
384 return original;
385 }
386
387 char[] originalChars = original.toCharArray();
388 char firstLetter =
389 Character.toUpperCase(original.charAt(0));
390 originalChars[0] = firstLetter;
391
392 return new String(originalChars);
393 }
394
395
396 /***
397 * <p>
398 * This will indicate whether the supplied name is a legal Java class
399 * name.
400 * </p>
401 *
402 * @param className the Java class name to check
403 * @return <code>boolean</code> - whether the supplied class is a legal
404 * Java class name.
405 */
406 public static boolean isLegalJavaClassName(String className) {
407 if (className == null) {
408 throw new IllegalArgumentException("A non-null String must be " +
409 "supplied to NamingUtils methods.");
410 }
411
412 for (int i = 0, len = className.length(); i < len; i++) {
413 if ((i == 0) &&
414 (!Character.isJavaIdentifierStart(className.charAt(i)))) {
415 return false;
416 } else if (!Character.isJavaIdentifierPart(className.charAt(i))) {
417 return false;
418 }
419 }
420
421 // If we got here, all is OK
422 return true;
423 }
424
425
426 /***
427 * <p>
428 * This will indicate whether the supplied name is a legal Java package
429 * name.
430 * </p>
431 *
432 * @param packageName the Java class name to check
433 * @return <code>boolean</code> - whether the supplied package is a legal
434 * Java package name.
435 */
436 public static boolean isLegalJavaPackageName(String packageName) {
437 <b>if (packageName == null) {
438 throw new IllegalArgumentException("A non-null String must be " +
439 "supplied to NamingUtils methods.");
440 }
441
442 for (int i = 0, len = packageName.length(); i < len; i++) {
443 if ((i == 0) &&
444 (!Character.isJavaIdentifierStart(packageName.charAt(i)))) {
445 return false;
446 } else if (
447 (!Character.isJavaIdentifierPart(packageName.charAt(i))) &&
448 (!(packageName.charAt(i) == '.'))) {
449
450 return false;
451 }
452 }
453
454 // If we got here, all is OK
455 return true;
456 }
457
458
459 /***
460 * <p>
461 * This will take a <code>String</code> with unknown capitalization,
462 * and convert the first letter to a capital letter, while converting
463 * all other letters to lowercase. Note that if you want the rest of
464 * the letters left alone, then
465 * <code>{@link #initialUpper(String)}</code> should be used.
466 * </p>
467 *
468 * @param original <code>String</code> to convert.
469 * @return <code>String</code> - the converted <code>String</code>.
470 */
471 public static String justInitialUpper(String original) {
472 if (original == null) {
473 throw new IllegalArgumentException("A non-null String must be " +
474 "supplied to CapitalizationUtils methods.");
475 }
476
477 String lowercaseOriginal = allLower(original);
478 return initialUpper(lowercaseOriginal);
479 }
480
481
482 /***
483 * <p>
484 * This will take a fully qualified Java class, and return just the name
485 * of the class, without any package qualifier. For example, invoking
486 * <code>removePackage("java.util.List");</code> would return
487 * "List".
488 * </p>
489 *
490 * @param className the Java class name to remove the package from.
491 * @return <code>String</code> - the Java class, without package.
492 */
493 public static String removePackage(String className) {
494 String returnValue = className;
495
496 if (returnValue.indexOf(".") > -1) {
497 returnValue = returnValue.substring(
498 returnValue.lastIndexOf(".") + 1);
499 }
500
501 return returnValue;
502 }
503
504
505 /***
506 * <p>
507 * Maps the given key to the given value. This value will replace any
508 * occurence of the key when a name contains illegal characters
509 * for a Java identifier.
510 * </p>
511 *
512 * @param key the key to map the given value to.
513 * @param value the value to map the given key to.
514 */
515 public static void setCharMapping(Character key, String value) {
516 if (key == null) {
517 throw new IllegalArgumentException("A non-null char key must be " +
518 "supplied to NamingUtils methods.");
519 }
520 if (value == null) {
521 throw new IllegalArgumentException("A non-null value must be " +
522 "supplied to NamingUtils methods.");
523 }
524
525 illegalCharMap.put(key, value);
526 }
527
528
529 /***
530 * <p>
531 * Checks whether each character in the given name is valid for a Java
532 * identifier. If there are illegal characters, the name is returned
533 * with each illegal character replaced with its value in the internal
534 * map of illegal <code>Character</code>s to replacement
535 * <code>String</code>s, else the same name is returned. If the
536 * illegal character can be interpreted as a separator character
537 * (such as the '-' in my-attribute), then the character following the
538 * separator character will be capitalized (i.e. my-attribute becomes
539 * myAttribute).
540 * </p>
541 *
542 * @param name the name to check.
543 * @return <code>String</code> - the converted name if illegal characters
544 * are present, else the same name.
545 */
546 private static String convertIllegalChars(String name) {
547 if (name == null) {
548 throw new IllegalArgumentException("A non-null name must " +
549 "be supplied to NamingUtils methods.");
550 }
551
552 StringBuffer verifiedName = new StringBuffer();
553 char[] characters = name.toCharArray();
554
555 // XXX: Uppercase the next letter after all illegal chars, or just the
556 // ones that can be interpreted as separator chars?
557 boolean upperNext = false;
558
559 for (int i = 0; i < characters.length; i++) {
560 char c = characters[i];
561
562 if (i == 0) {
563 if (Character.isJavaIdentifierStart(c) == false) {
564 String legalValue =
565 (String) illegalCharMap.get(new Character(c));
566
567 if (legalValue == null) {
568
569 // XXX: Throw an exception or append DEFAULT_MAPPING?
570 throw new IllegalArgumentException("No mapping for " +
571 "illegal character: " + c);
572 } else {
573 verifiedName.append(legalValue);
574
575 // XXX Does it make sense to uppercase the next char
576 // if the illegal char is the first char of the
577 // identifier?
578 upperNext = true;
579 }
580 } else {
581 verifiedName.append(c);
582 }
583 } else {
584 if (Character.isJavaIdentifierPart(c) == false) {
585 String legalValue =
586 (String) illegalCharMap.get(new Character(c));
587
588 if (legalValue == null) {
589
590 // XXX: Throw an exception or append DEFAULT_MAPPING?
591 throw new IllegalArgumentException("No mapping for " +
592 "illegal character: " + c);
593 } else {
594 verifiedName.append(legalValue);
595 upperNext = true;
596 }
597 } else {
598 if (upperNext) {
599 verifiedName.append(Character.toUpperCase(c));
600 upperNext = false;
601 } else {
602 verifiedName.append(c);
603 }
604 }
605 }
606 }
607 return verifiedName.toString();
608 }
609
610
611 /***
612 * <p>
613 * Checks whether the given name collides with a Java reserved word. If
614 * there is a collision, the name is returned prefixed with
615 * <code>{@link #XML_PREFIX}</code>, else it is returned unchanged.
616 * </p>
617 *
618 * @param name the name to check.
619 * @return <code>String</code> - the converted name if there is a collision
620 * with a Java reserved word, else the same name.
621 */
622 private static String convertReservedWord(String name) {
623 if (name == null) {
624 throw new IllegalArgumentException("A non-null name must " +
625 "be supplied to NamingUtils methods.");
626 }
627
628 if (reservedWords.get(name) == null) {
629 return name;
630 } else {
631 return new StringBuffer().append(XML_PREFIX)
632 .append(name).toString();
633 }
634 }
635
636 // Initialize the reserved words map and the illegal characters map.
637 static {
638 for (int i = 0; i < reservedWordsArray.length; i++) {
639 reservedWords.put(reservedWordsArray[i], new Integer(i));
640 }
641 reservedWordsArray = null;
642
643 // XXX: Add more illegal chars mappings...we don't have to check too
644 // many because we can assume XML naming rules, though
645 illegalCharMap.put(new Character('.'), DEFAULT_MAPPING);
646 illegalCharMap.put(new Character('-'), DEFAULT_MAPPING);
647 illegalCharMap.put(new Character(':'), DEFAULT_MAPPING);
648 }
649 }
This page was automatically generated by Maven